using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.SmartParts;
using Microsoft.Practices.CompositeUI.Utility;
using Microsoft.Practices.ObjectBuilder;

namespace SCSFContrib.CompositeUI.WPF.Workspaces
{
	/// <summary>
	/// Implements an <see cref="IWorkspace"/> that displays smart parts in <see cref="TabItem"/>s.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The <c>TabWorkspace</c> displays smart parts with arbitrary <see cref="object"/> contents inside <see cref="TabItem"/>s. It accepts
	/// instances of <see cref="TabSmartPartInfo"/> to tailor the tabs that contains the shown smart part.
	/// </para>
	/// </remarks>
	/// <example>
	/// The following example shows how a <c>TabWorkspace</c> can be declared in XAML:
	/// <code>
	/// <![CDATA[
	///	<cab:TabWorkspace x:Name="_tabWorkspace"/>
	/// ]]>
	/// </code>
	/// </example>
	/// <example>
	/// The following example shows how smart parts can be manipulated in a <c>TabWorkspace</c>:
	/// <code>
	/// //show a smart part in a tab titled 'TAB 1'
	/// _tabWorkspace.Show(smartPart1, new TabSmartPartInfo("TAB 1"));
	///
	/// //show a smart part in a tab with a button as its header
	/// _tabWorkspace.Show(smartPart2, new TabSmartPartInfo(new Button()));
	///
	/// //show a smart part in a tab and ensure the tab appears before other tabs but is not activated
	/// TabSmartPartInfo tspi = new TabSmartPartInfo("FIRST TAB");
	/// tspi.Position = TabPosition.Beginning;
	/// tspi.Activate = false;
	/// _tabWorkspace.Show(smartPart3, tspi);
	/// </code>
	/// </example>
	public class TabWorkspace : TabControl, IComposableWorkspace<object, TabSmartPartInfo>
	{
		private readonly WorkspaceComposer<object, TabSmartPartInfo> _composer;
		private readonly IDictionary<object, TabItem> _tabItems;

		/// <summary>
		/// Gets a read-only dictionary of the smart part to <see cref="TabItem"/> relationships maintained by this <c>TabWorkspace</c>.
		/// </summary>
		public ReadOnlyDictionary<object, TabItem> TabItems
		{
			get
			{
				return new ReadOnlyDictionary<object,TabItem>(_tabItems);
			}
		}
		
		/// <summary>
		/// Constructs an instance of <c>TabWorkspace</c>.
		/// </summary>
		public TabWorkspace()
		{
			_composer = new WorkspaceComposer<object, TabSmartPartInfo>(this);
			_tabItems = new Dictionary<object, TabItem>();
		}

		/// <summary>
		/// Provides dependencies to this <c>ContentControlWorkspace</c>.
		/// </summary>
		/// <param name="workItem">
		/// The <see cref="WorkItem"/>.
		/// </param>
		[InjectionMethod]
		public void InjectDependencies(WorkItem workItem)
		{
			_composer.WorkItem = workItem;
		}

		/// <summary>
		/// Applies the specified smart part info to the smart part in this workspace.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to which <paramref name="smartPartInfo"/> will be applied.
		/// </param>
		/// <param name="smartPartInfo">
		/// The <typeparamref name="TSmartPartInfo"/> instance to apply to <paramref name="smartPart"/>.
		/// </param>
		public virtual void OnApplySmartPartInfo(object smartPart, TabSmartPartInfo smartPartInfo)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");
			Guard.ArgumentNotNull(smartPartInfo, "smartPartInfo");

			if (!_tabItems.ContainsKey(smartPart))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			smartPartInfo.ApplyTo(_tabItems[smartPart]);
		}

		/// <summary>
		/// Converts <paramref name="source"/> into an instance of <typeparamref name="TSmartPartInfo"/>.
		/// </summary>
		/// <param name="source">
		/// The instance of <see cref="ISmartPartInfo"/> to be converted.
		/// </param>
		/// <returns>
		/// <paramref name="source"/> converted to an instance of <typeparamref name="TSmartPartInfo"/>.
		/// </returns>
		protected virtual TabSmartPartInfo OnConvertFrom(ISmartPartInfo source)
		{
			TabSmartPartInfo tabSmartPartInfo = SmartPartInfo.ConvertTo<TabSmartPartInfo>(source);
			TabSmartPartInfo sourceTabSmartPartInfo = source as TabSmartPartInfo;

			if (sourceTabSmartPartInfo != null)
			{
				sourceTabSmartPartInfo.ApplyTo(tabSmartPartInfo);
			}

			return tabSmartPartInfo;
		}
		
		/// <summary>
		/// Shows the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to show.
		/// </param>
		/// <param name="smartPartInfo">
		/// The instance of <typeparamref name="TSmartPartInfo"/> that provides any extraneous information to be used in the displaying of
		/// <paramref name="smartPart"/>.
		/// </param>
		public virtual void OnShow(object smartPart, TabSmartPartInfo smartPartInfo)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");
			Guard.ArgumentNotNull(smartPartInfo, "smartPartInfo");

			if (_tabItems.ContainsKey(smartPart))
			{
				throw new ArgumentException("smartPart is already present in this workspace.", "smartPart");
			}

			TabItem tabItem = new TabItem();
			smartPartInfo.ApplyTo(tabItem);
			tabItem.Content = smartPart;
			_tabItems[smartPart] = tabItem;
			Items.Insert(smartPartInfo.Position == TabPosition.End ? Items.Count : 0, tabItem);

			if (smartPartInfo.Activate)
			{
				Activate(smartPart);
			}
		}

		/// <summary>
		/// Activates the specified smart part in this workspace.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to activate.
		/// </param>
		public virtual void OnActivate(object smartPart)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");
			TabItem tabItem;

			if (!_tabItems.TryGetValue(smartPart, out tabItem))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			tabItem.Visibility = Visibility.Visible;
			tabItem.IsSelected = true;
		}

		/// <summary>
		/// Hides the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part in this workspace to be hidden.
		/// </param>
		public virtual void OnHide(object smartPart)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");
			TabItem tabItem;

			if (!_tabItems.TryGetValue(smartPart, out tabItem))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			tabItem.Visibility = Visibility.Collapsed;
			tabItem.IsSelected = false;
		}

		/// <summary>
		/// Closes the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part in this workspace that should be closed.
		/// </param>
		public virtual void OnClose(object smartPart)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");
			TabItem tabItem;

			if (!_tabItems.TryGetValue(smartPart, out tabItem))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			tabItem.Content = null;
			Items.Remove(tabItem);
			_tabItems.Remove(smartPart);
		}

		/// <summary>
		/// Handles any selection changed behavior for this <c>TabWorkspace</c>.
		/// </summary>
		/// <param name="e">
		/// Event arguments.
		/// </param>
		protected override void OnSelectionChanged(SelectionChangedEventArgs e)
		{
			base.OnSelectionChanged(e);
			object smartPart = SelectedContent;

			if ((smartPart != null) && _tabItems.ContainsKey(smartPart))
			{
				_composer.SetActiveSmartPart(smartPart);
			}
			else
			{
				_composer.SetActiveSmartPart(null);
			}
		}

		#region Composable Workspace Implementation

		/// <summary>
		/// Gets the active smart part in this workspace, or <see langword="null"/> if no smart part is active.
		/// </summary>
		public object ActiveSmartPart
		{
			get
			{
				return _composer.ActiveSmartPart;
			}
		}

		/// <summary>
		/// Gets a snapshot of the current smart parts contained in this workspace.
		/// </summary>
		public ReadOnlyCollection<object> SmartParts
		{
			get
			{
				return _composer.SmartParts;
			}
		}

		/// <summary>
		/// Gets the collection of smart parts being managed by this workspace.
		/// </summary>
		protected IList<object> InnerSmartParts
		{
			get
			{
				return _composer.InnerSmartParts;
			}
		}

		/// <summary>
		/// Occurs whenever a smart part is activated by this workspace.
		/// </summary>
		public event EventHandler<WorkspaceEventArgs> SmartPartActivated;

		/// <summary>
		/// Occurs whenever a smart part is about to be closed by this workspace.
		/// </summary>
		public event EventHandler<WorkspaceCancelEventArgs> SmartPartClosing;

		/// <summary>
		/// Activates the specified smart part in this workspace.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to activate.
		/// </param>
		public void Activate(object smartPart)
		{
			_composer.Activate(smartPart);
		}

		/// <summary>
		/// Applies the specified smart part info to the smart part in this workspace.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to which <paramref name="smartPartInfo"/> will be applied.
		/// </param>
		/// <param name="smartPartInfo">
		/// The <typeparamref name="TSmartPartInfo"/> instance to apply to <paramref name="smartPart"/>.
		/// </param>
		public void ApplySmartPartInfo(object smartPart, ISmartPartInfo smartPartInfo)
		{
			_composer.ApplySmartPartInfo(smartPart, smartPartInfo);
		}

		/// <summary>
		/// Closes the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part in this workspace that should be closed.
		/// </param>
		public void Close(object smartPart)
		{
			_composer.Close(smartPart);
		}

		/// <summary>
		/// Hides the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part in this workspace to be hidden.
		/// </param>
		public void Hide(object smartPart)
		{
			_composer.Hide(smartPart);
		}

		/// <summary>
		/// Shows the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to show.
		/// </param>
		public void Show(object smartPart)
		{
			_composer.Show(smartPart);
		}

		/// <summary>
		/// Shows the specified smart part.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to show.
		/// </param>
		/// <param name="smartPartInfo">
		/// The instance of <typeparamref name="TSmartPartInfo"/> that provides any extraneous information to be used in the displaying of
		/// <paramref name="smartPart"/>.
		/// </param>
		public void Show(object smartPart, ISmartPartInfo smartPartInfo)
		{
			_composer.Show(smartPart, smartPartInfo);
		}

		/// <summary>
		/// Converts from <paramref name="source"/> to <typeparamref name="TSmartPartInfo"/>.
		/// </summary>
		/// <param name="source">
		/// The <see cref="ISmartPartInfo"/> instance to convert.
		/// </param>
		/// <returns>
		/// An instance of type <typeparamref name="TSmartPartInfo"/>.
		/// </returns>
		TabSmartPartInfo IComposableWorkspace<object, TabSmartPartInfo>.ConvertFrom(ISmartPartInfo source)
		{
			return OnConvertFrom(source);
		}

		/// <summary>
		/// Raises the <see cref="SmartPartActivated"/> event.
		/// </summary>
		/// <param name="e">
		/// Event arguments.
		/// </param>
		void IComposableWorkspace<object, TabSmartPartInfo>.RaiseSmartPartActivated(WorkspaceEventArgs e)
		{
			OnSmartPartActivated(e);
		}

		/// <summary>
		/// Raises the <see cref="SmartPartClosing"/> event.
		/// </summary>
		/// <param name="e">
		/// Event arguments.
		/// </param>
		void IComposableWorkspace<object, TabSmartPartInfo>.RaiseSmartPartClosing(WorkspaceCancelEventArgs e)
		{
			OnSmartPartClosing(e);
		}

		/// <summary>
		/// Raises the <see cref="SmartPartActivated"/> event.
		/// </summary>
		/// <param name="e">
		/// Event arguments.
		/// </param>
		protected virtual void OnSmartPartActivated(WorkspaceEventArgs e)
		{
			EventHandler<WorkspaceEventArgs> handler = SmartPartActivated;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Raises the <see cref="SmartPartClosing"/> event.
		/// </summary>
		/// <param name="e">
		/// Event arguments.
		/// </param>
		protected virtual void OnSmartPartClosing(WorkspaceCancelEventArgs e)
		{
			EventHandler<WorkspaceCancelEventArgs> handler = SmartPartClosing;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		#endregion
	}
}
